<template>
  <div
    class="file-upload bg-light"
    @click.prevent="triggerUpload"
    v-bind="$attrs"
  >
    <!-- 有三个插槽，父组件可以选择其中的一个来替换 -->
    <slot v-if="fileStatus === 'loading'" name="loading">
      <button class="btn btn-primary" disabled>正在上传...</button>
    </slot>
    <!-- 该插槽绑定了数据，父组件可以直接通过<template #uploaded="dataProps"></template>拿到uploadedData -->
    <slot
      v-else-if="fileStatus === 'success'"
      name="uploaded"
      :uploadedData="uploadedData"
    >
      <button class="btn btn-primary">上传成功</button>
    </slot>
    <slot v-else name="default">
      <button class="btn btn-primary">点击上传</button>
    </slot>
  </div>
  <input
    type="file"
    class="file-input d-none"
    ref="fileInput"
    @change="handleFileChange"
  />
</template>
<script lang='ts'>
import { defineComponent, ref, PropType, watch } from "vue";
import axios from "axios";
// 上传过程中不同的状态要显示不同的内容，用字段UploadStatus来表示
type UploadStatus = "ready" | "loading" | "success" | "error";
type beforeUploadProps = (file: File) => boolean;
export default defineComponent({
  components: {},
  props: {
    action: {
      type: String,
      required: true,
    },
    beforeUpload: {
      type: Function as PropType<beforeUploadProps>,
    },
    uploaded: {
      type: Object,
    },
  },
  inheritAttrs: false,
  emits: ["file-uploaded", "file-uploaded-error"],
  setup(props, context) {
    // 将文件上传的input框隐藏，然后点击button触发input框的点击事件
    const fileInput = ref<null | HTMLInputElement>(null);
    const fileStatus = ref<UploadStatus>("ready");
    let uploadedData = ref(props.uploaded); // 上传成功的内容
    const triggerUpload = () => {
      if (fileInput.value) {
        fileInput.value.click();
      }
    };
    // 监听传过来的uploaded，避免在编辑模式下，值还没有到，uploadedData为空的情况
    watch(
      () => props.uploaded,
      () => {
        if (props.uploaded) {
          fileStatus.value = "success";
          uploadedData.value = props.uploaded;
        }
      }
    );
    const handleFileChange = (e: Event) => {
      const target = e.target as HTMLInputElement;
      if (target.files) {
        fileStatus.value = "loading";
        const files = Array.from(target.files); // 将文件列表转换为数组
        // 判断是否验证通过
        if (props.beforeUpload) {
          const result = props.beforeUpload(files[0]);
          if (!result) {
            return;
          }
        }
        const formData = new FormData();
        formData.append("file", files[0]);
        axios
          .post(props.action, formData, {
            headers: {
              "Content-Type": "multipart/form-data",
            },
          })
          .then((resp) => {
            fileStatus.value = "success";
            uploadedData.value = resp.data;
            context.emit("file-uploaded", resp.data);
            console.log(resp);
          })
          .catch((error) => {
            fileStatus.value = "error";
            context.emit("file-uploaded-error", { error });
          })
          .finally(() => {
            // 最终都要将input框的内容清空
            if (fileInput.value) {
              // 第一个value是因为ref， 第二个是dom节点上的值
              fileInput.value.value = "";
            }
          });
      }
    };
    return {
      fileInput,
      triggerUpload,
      handleFileChange,
      fileStatus,
      uploadedData,
    };
  },
});
</script>
<style scoped>
.file-upload {
  height: 200px;
  cursor: pointer;
  overflow: hidden;
}
</style>